package com.pobox.tupletest;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class TupleTester {
    /**
     * Logger for this class
     */
    private static final Logger logger = Logger.getLogger(TupleTester.class);

    private Random randomizer = new Random(System.currentTimeMillis());
    private int nFinal; // the strength/num items in a tuple
    private List<Tuple> wantedTuples;
    private List<Test> tests = new ArrayList<Test>();
    private Table table;

    private static final int MAX_ITERS = 10;

    /**
     * Construct a new instance which will default to All-Pairs of features
     * (n-Tuple = 2 )
     */
    public TupleTester() {
        this(2);
    }

    /**
     * Construct a new instance with n-Tuple = paramater
     */
    public TupleTester(int numberOfItemsInTuple) {
        assert numberOfItemsInTuple > 0 : "value for number of items in tuple cannot be negative";
        nFinal = numberOfItemsInTuple;
    }

    public int getNFinal() {
        return nFinal;
    }

    public void setNFinal(int final1) {
        logger.debug("setNFinal(int) - start - set to:" + final1);

        nFinal = final1;
    }

    public void addTest(Test aTest) {
        logger.debug("addTest(Test) - start - adding: " + aTest);

        assert aTest != null : "cannot add null to tests";
        tests.add(aTest);

        logger.debug("addTest(Test) - end");
    }

    public void removeTest(Test aTest) {
        logger.debug("removeTest(Test) - start - removing: " + aTest);

        assert aTest != null : "cannot remove null from tests";
        assert tests.contains(aTest) : "cannot remove a test that's not there";
        tests.remove(aTest);

        logger.debug("removeTest(Test) - end");
    }

    public String reportAllTests() {
        logger.debug("reportAllTests() - start");

        StringBuilder sb = new StringBuilder();
        for (Test test : getTests()) {
            sb.append(test.report());
            sb.append("\n");
        }
        String returnString = sb.toString();
        logger.trace(returnString);
        logger.debug("reportAllTests() - end");
        return returnString;
    }

    public List<Test> getTests() {
        return tests;
    }

    public Table getTable() {
        return table;
    }

    public void setTable(Table table) {
        logger.debug("setTable(Table) - start");

        assert null != table : "cannot use a null table";
        this.table = table;
        this.wantedTuples = table.getAllWantedTuples(nFinal);

        logger.debug("setTable(Table) - end");
    }

    public int generateTest(Table table) {
        logger.debug("generateTest(Table) - start");

        Test test = null;
        for (int i = 0; i < MAX_ITERS; ++i) {
            test = randomTestCase();
            Tuple tuple = table
                    .getAllWantedTuples(table.getItemComboPerTuple()).get(0);
            test.pluginTupleToTestCase(tuple);
            for (Item item : tuple) {
                test.setDimensionChangePermission(item.getDimension(), false);
            }
            // TODO withouts
            break;
        }

        int coverage = test.maximizeCoverage(table);
        addTest(test);

        logger.debug("generateTest(Table) - end");
        return coverage;
    }

    Random getRandomizer() {
        return randomizer;
    }

    void setRandomizer(Random randomizer) {
        assert randomizer != null : "Cannot use a null Random";
        this.randomizer = randomizer;
    }

    int nextRandomInt(int modValue) {
        int returnint = (Math.abs(randomizer.nextInt()) % modValue) + 1;
        return returnint;
    }

    Test randomTestCase() {
        logger.debug("randomTestCase() - start");

        assert null != table : "table is null";
        Test test = new Test();
        for (Dimension dimension : table) {
            int randomFeature = nextRandomInt(dimension.size()) - 1;
            Item item = dimension.get(randomFeature);
            test.add(item);
        }

        logger.debug("randomTestCase() - end - generated:" + test);
        return test;
    }

    void driver(String args[]) {
        logger.debug("driver(String[]) - start");

        // if (parse(args) { /* read the user's instructions */
        coverTuples(); /* generate testcases until all tuples are covered */
        reportAllTests(); /* report the results */

        logger.debug("driver(String[]) - end");
    }

    public static void main(String args[]) {
        logger.debug("main(String[]) - start");

        TupleTester jenny = new TupleTester();
        jenny.driver(args);

        logger.debug("main(String[]) - end");
    }

    void coverTuples() {
        logger.debug("coverTuples() - start");

        final int GROUP_SIZE = 5;

        Test currTest = new Test();

        while (table.size() > 0) {
            int bestCount = -1;

            /* find a good test */
            for (int j = 0; j < GROUP_SIZE; ++j) {
                int this_count = 0;
                /* generate a test that covers the first tuple */
                this_count = generateTest(table);
            }
            break;
        }

        logger.debug("coverTuples() - end");
    }

}
